/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package lineage.model.instance;

import java.sql.Timestamp;

import lineage.controller.pc.L1PcController;
import lineage.core.L1GameClient;
import lineage.database.tables.ExpTable;
import lineage.model.L1Character;
import lineage.model.L1Object;
import lineage.model.gameworld.L1World;
import lineage.packet.L1AbstractPacketServer;
import lineage.packet.server.S_CharPackOther;
import lineage.packet.server.S_ObjectRemove;
import lineage.util.L1Point;
import lineage.util.RangeInt;

/**
 * 玩家.
 * 
 * @version 2014年3月17日下午11:44:21
 * @author jrwz
 */
public final class L1PcInstance extends L1Character {
    private static final long serialVersionUID = 557939477281829437L;
    /** 各职业编号 - 王族. */
    private static final int TYPE_ID_CROWN = 0;
    /** 各职业编号 - 骑士. */
    private static final int TYPE_ID_KNIGHT = 1;
    /** 各职业编号 - 精灵. */
    private static final int TYPE_ID_ELF = 2;
    /** 各职业编号 - 法师. */
    private static final int TYPE_ID_WIZARD = 3;
    /** 各职业编号 - 黑妖. */
    private static final int TYPE_ID_DARK_ELF = 4;
    /** 各职业编号 - 龙骑士. */
    private static final int TYPE_ID_DRAGON_KNIGHT = 5;
    /** 各职业编号 - 幻术师. */
    private static final int TYPE_ID_ILLUSIONIST = 6;
    /** 角色的性别. */
    private int sex;
    /** 角色的网络连接. */
    private L1GameClient netConnection;
    /** 角色的血盟名称. */
    private String clanName;
    /** 角色的职业. */
    private int type;
    /** 角色的默认外形. */
    private int classId;
    /** 角色的血盟编号. */
    private int clanId;
    /** 角色的血盟阶级. */
    private int clanRank;
    /** 角色的权限. */
    private int accessLevel;
    /** 角色的初始属性：力量. */
    private int originalStr;
    /** 角色的初始属性：体质. */
    private int originalCon;
    /** 角色的初始属性：敏捷. */
    private int originalDex;
    /** 角色的初始属性：精神. */
    private int originalWis;
    /** 角色的初始属性：智力. */
    private int originalInt;
    /** 角色的初始属性：魅力. */
    private int originalCha;
    /** 角色的创建时间(生日). */
    private Timestamp createTime;
    /** 角色的帐号名称. */
    private String accountName;
    /** 角色的删除时间. */
    private Timestamp deleteTime;
    /** 备用职业类型(该值永不改变). */
    private int spareType;
    /** 表情动作. */
    private int actionEmotes;

    // 当有对象进入自己屏幕范围时调用
    @Override
    public void toPerceive(final L1Object object) {
        if (object == null) {
            return;
        }

        L1PcInstance tgPc = (L1PcInstance) object;

        if (!tgPc.isKnownsPlayer(this)) {
            tgPc.addKnownPlayer(this);
            tgPc.sendPackets(new S_CharPackOther(this)); // 向对方发送自身封包
        }
        if (!this.isKnownsPlayer(tgPc)) { // TODO
            this.addKnownPlayer(tgPc);
            this.sendPackets(new S_CharPackOther(tgPc));
        }
    }

    /**
     * 删除离开可视范围(屏幕范围外)的对象.
     */
    private void removeOutOfRangeObjects() {
        // 已认识角色
        getKnownPlayers().forEach(knownPc -> {
            if (knownPc != null) {
                if (!getLocation().isInScreen(knownPc.getLocation())) { // 屏幕范围外
                    removeKnownPlayer(knownPc);
                    sendPackets(new S_ObjectRemove(knownPc));
                    if (knownPc.isKnownsPlayer(this)) { // TODO
                        knownPc.removeKnownPlayer(this);
                        knownPc.sendPackets(new S_ObjectRemove(this));
                    }
                }
            }
        });
    }

    /**
     * 更新可见对象.
     */
    public void updateObject() {
        removeOutOfRangeObjects(); // 删除离开画面的对象
        // 屏幕范围内的'所有角色'(排除自身)
        L1World.getInstance().getVisiblePlayer(this).forEach(pc -> pc.toPerceive(this));
    }

    @Override
    public boolean toMove(final int x, final int y, final int heading) {
        // TODO 相关设置及检查
        // 取得移动前坐标
        final int tempX = getX();
        final int tempY = getY();
        final int tempHeading = getHeading();

        // 计算移动后坐标
        final int newX = x + L1Point.HEADING_TABLE_X[heading];
        final int newY = y + L1Point.HEADING_TABLE_Y[heading];

        if (x != tempX && y != tempY) {
            return false;
        }

        // 能否通过
        final boolean isPassable = getMap().isPassable(tempX, tempY, heading);
        if (!isPassable) {
            return false;
        }

        // 记录移动前坐标
        setTempX(tempX);
        setTempY(tempY);
        setTempHeading(tempHeading);
        return super.toMove(newX, newY, heading);
    }

    @Override
    public void setHp(int hp) {
        this.hp = hp;
        this.hp = RangeInt.ensure(this.hp, 0, Short.MAX_VALUE); // 最低0、最高32767
    }

    @Override
    public void setMp(int mp) {
        this.mp = mp;
        this.mp = RangeInt.ensure(this.mp, 0, Short.MAX_VALUE); // 最低0、最高32767
    }

    @Override
    public void setMaxHP(int maxHP) {
        this.maxHP = maxHP;
        this.maxHP = RangeInt.ensure(this.maxHP, 1, Short.MAX_VALUE); // 最低1、最高32767
    }

    @Override
    public void setMaxMP(int maxMP) {
        this.maxMP = maxMP;
        this.maxMP = RangeInt.ensure(this.maxMP, 0, Short.MAX_VALUE); // 最低0、最高32767
    }

    @Override
    public void setLevel(int level) {
        this.level = level;
        this.level = RangeInt.ensure(this.level, 1, L1PcController.getMaxLevel()); // 最低1、最高201
    }

    /**
     * 将数据存进数据库(仅角色本身).
     * 
     * @return 如果储存成功则返回true
     */
    public boolean save() {
        return L1PcController.storeCharacter(this);
    }

    /**
     * 取得角色的性别.
     * 
     * @return 角色的性别
     */
    public int getSex() {
        return sex;
    }

    /**
     * 设置角色的性别.
     * 
     * @param sex
     *            - 要设置的性别
     */
    public void setSex(int sex) {
        this.sex = sex;
    }

    /**
     * 取得角色的网络连接.
     * 
     * @return 角色的网络连接
     */
    public L1GameClient getNetConnection() {
        return netConnection;
    }

    /**
     * 设置角色的网络连接.
     * 
     * @param netConnection
     *            - 要设置的网络连接
     */
    public void setNetConnection(L1GameClient netConnection) {
        this.netConnection = netConnection;
    }

    /**
     * 角色的网络连接是否正常.
     * 
     * @return 如果正常则返回true
     */
    public boolean isNetConnection() {
        return netConnection != null;
    }

    /**
     * 发送封包：单体
     * 
     * @param packet
     *            - 要发送的服务端封包
     */
    public void sendPackets(final L1AbstractPacketServer packet) {
        if (isNetConnection()) {
            netConnection.sendPacket(packet);
        }
    }

    /**
     * 取得角色的血盟名称.
     * 
     * @return 角色的血盟名称
     */
    public String getClanName() {
        return clanName;
    }

    /**
     * 设置角色的血盟名称.
     * 
     * @param clanName
     *            - 要设置的血盟名称
     */
    public void setClanName(String clanName) {
        this.clanName = clanName;
    }

    /**
     * 取得角色的职业.
     * 
     * @return 角色的职业
     */
    public int getType() {
        return type;
    }

    /**
     * 设置角色的职业.
     * 
     * @param type
     *            - 要设置的职业
     */
    public void setType(int type) {
        this.type = type;
    }

    /**
     * 取得角色的默认外形.
     * 
     * @return 角色的默认外形
     */
    public int getClassId() {
        return classId;
    }

    /**
     * 设置角色的默认外形.
     * 
     * @param classId
     *            - 要设置的默认外形
     */
    public void setClassId(int classId) {
        this.classId = classId;
    }

    /**
     * 取得角色的血盟编号.
     * 
     * @return 角色的血盟编号
     */
    public int getClanId() {
        return clanId;
    }

    /**
     * 设置角色的血盟编号.
     * 
     * @param clanId
     *            - 要设置的血盟编号
     */
    public void setClanId(int clanId) {
        this.clanId = clanId;
    }

    /**
     * 取得角色的血盟阶级.
     * 
     * @return 角色的血盟阶级
     */
    public int getClanRank() {
        return clanRank;
    }

    /**
     * 设置角色的血盟阶级.
     * 
     * @param clanRank
     *            - 要设置的血盟阶级
     */
    public void setClanRank(int clanRank) {
        this.clanRank = clanRank;
    }

    /**
     * 取得角色的权限.
     * 
     * @return 角色的权限
     */
    public int getAccessLevel() {
        return accessLevel;
    }

    /**
     * 设置角色的权限.
     * 
     * @param accessLevel
     *            - 要设置的权限
     */
    public void setAccessLevel(int accessLevel) {
        this.accessLevel = accessLevel;
    }

    /**
     * 取得角色的初始属性：力量.
     * 
     * @return 角色的初始属性：力量
     */
    public int getOriginalStr() {
        return originalStr;
    }

    /**
     * 设置角色的初始属性：力量.
     * 
     * @param originalStr
     *            - 要设置的初始属性：力量
     */
    public void setOriginalStr(int originalStr) {
        this.originalStr = originalStr;
    }

    /**
     * 取得角色的初始属性：体质.
     * 
     * @return 角色的初始属性：体质
     */
    public int getOriginalCon() {
        return originalCon;
    }

    /**
     * 设置角色的初始属性：体质.
     * 
     * @param originalCon
     *            - 要设置的初始属性：体质
     */
    public void setOriginalCon(int originalCon) {
        this.originalCon = originalCon;
    }

    /**
     * 取得角色的初始属性：敏捷.
     * 
     * @return 角色的初始属性：敏捷
     */
    public int getOriginalDex() {
        return originalDex;
    }

    /**
     * 设置角色的初始属性：敏捷.
     * 
     * @param originalDex
     *            - 要设置的初始属性：敏捷
     */
    public void setOriginalDex(int originalDex) {
        this.originalDex = originalDex;
    }

    /**
     * 取得角色的初始属性：精神.
     * 
     * @return 角色的初始属性：精神
     */
    public int getOriginalWis() {
        return originalWis;
    }

    /**
     * 设置角色的初始属性：精神.
     * 
     * @param originalWis
     *            - 要设置的初始属性：精神
     */
    public void setOriginalWis(int originalWis) {
        this.originalWis = originalWis;
    }

    /**
     * 取得角色的初始属性：智力.
     * 
     * @return 角色的初始属性：智力
     */
    public int getOriginalInt() {
        return originalInt;
    }

    /**
     * 设置角色的初始属性：智力.
     * 
     * @param originalInt
     *            - 要设置的初始属性：智力
     */
    public void setOriginalInt(int originalInt) {
        this.originalInt = originalInt;
    }

    /**
     * 取得角色的初始属性：魅力.
     * 
     * @return 角色的初始属性：魅力
     */
    public int getOriginalCha() {
        return originalCha;
    }

    /**
     * 设置角色的初始属性：魅力.
     * 
     * @param originalCha
     *            - 要设置的初始属性：魅力
     */
    public void setOriginalCha(int originalCha) {
        this.originalCha = originalCha;
    }

    /**
     * 取得角色的创建时间(生日).
     * 
     * @return 角色的创建时间(生日)
     */
    public Timestamp getCreateTime() {
        return createTime;
    }

    /**
     * 设置角色的创建时间(生日).
     * 
     * @param createTime
     *            - 要设置的创建时间(生日)
     */
    public void setCreateTime(Timestamp createTime) {
        this.createTime = createTime;
    }

    /**
     * 取得角色的帐号名称.
     * 
     * @return 角色的帐号名称
     */
    public String getAccountName() {
        return accountName;
    }

    /**
     * 设置角色的帐号名称.
     * 
     * @param accountName
     *            - 要设置的帐号名称
     */
    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    /**
     * 取得角色的删除时间.
     * 
     * @return 角色的删除时间
     */
    public Timestamp getDeleteTime() {
        return deleteTime;
    }

    /**
     * 设置角色的删除时间.
     * 
     * @param deleteTime
     *            - 要设置的删除时间
     */
    public void setDeleteTime(Timestamp deleteTime) {
        this.deleteTime = deleteTime;
    }

    /**
     * 职业是否为：王族.
     */
    public boolean isCrown() {
        // return getClassId() == L1PcBean.CLASS_ID[getSex()][L1PcBean.TYPE_ID_CROWN];
        return getSpareType() == TYPE_ID_CROWN;
    }

    /**
     * 职业是否为：骑士.
     */
    public boolean isKnight() {
        return getSpareType() == TYPE_ID_KNIGHT;
    }

    /**
     * 职业是否为：精灵.
     */
    public boolean isElf() {
        return getSpareType() == TYPE_ID_ELF;
    }

    /**
     * 职业是否为：法师.
     */
    public boolean isWizard() {
        return getSpareType() == TYPE_ID_WIZARD;
    }

    /**
     * 职业是否为：黑妖.
     */
    public boolean isDarkElf() {
        return getSpareType() == TYPE_ID_DARK_ELF;
    }

    /**
     * 职业是否为：龙骑士.
     */
    public boolean isDragonKnight() {
        return getSpareType() == TYPE_ID_DRAGON_KNIGHT;
    }

    /**
     * 职业是否为：幻术师.
     */
    public boolean isIllusionist() {
        return getSpareType() == TYPE_ID_ILLUSIONIST;
    }

    /**
     * 取得备用职业类型(该值永不改变).
     * 
     * @return 职业
     */
    public int getSpareType() {
        return spareType;
    }

    /**
     * 设置备用职业类型(该值永不改变).
     * 
     * @param spareType
     *            - 要设置的职业
     */
    public void setSpareType(int spareType) {
        this.spareType = spareType;
    }

    /**
     * 取得表情动作.
     * 
     * @return 表情动作编号
     */
    public int getActionEmotes() {
        return actionEmotes;
    }

    /**
     * 设置表情动作.
     * 
     * @param actionEmotes
     *            - 要设置的动作编号
     */
    public void setActionEmotes(int actionEmotes) {
        this.actionEmotes = actionEmotes;
    }

    /**
     * 根据累积的总经验值重置当前的等级.
     * <p>
     * 如果等级与经验值不匹配则会升级或降级
     */
    private void resetLevel() {
        setLevel(ExpTable.getInstance().getLevelByExp(getExp()));
    }

    /**
     * 重置物理防御为初始值(刚创建角色时、根据初始敏捷加点).
     */
    private void resetOriginalAc() {
        setAc(L1PcController.getOriginalAc(this));
    }

    /**
     * 全属性重置.
     * <p>
     * 重置：等级<br>
     * 重置：初始物理防御值
     */
    public void refresh() {
        resetLevel();
        resetOriginalAc();
    }

    /**
     * 取得角色的初始能力加成值(力敏体精魅智).
     * 
     * @return 出生时的加点 减去 标准值的差
     */
    public int[] getOriginalUp() {
        final int originalStr = L1PcController.getOriginalStr(this);
        final int originalDex = L1PcController.getOriginalDex(this);
        final int originalCon = L1PcController.getOriginalCon(this);
        final int originalWis = L1PcController.getOriginalWis(this);
        final int originalCha = L1PcController.getOriginalCha(this);
        final int originalInt = L1PcController.getOriginalInt(this);

        final int pcStr = getOriginalStr();
        final int pcDex = getOriginalDex();
        final int pcCon = getOriginalCon();
        final int pcWis = getOriginalWis();
        final int pcCha = getOriginalCha();
        final int pcInt = getOriginalInt();

        final int upStr = pcStr - originalStr;
        final int upDex = pcDex - originalDex;
        final int upCon = pcCon - originalCon;
        final int upWis = pcWis - originalWis;
        final int upCha = pcCha - originalCha;
        final int upInt = pcInt - originalInt;

        final int result[] = { upStr, upDex, upCon, upWis, upCha, upInt };
        return result;
    }
}
